﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

using System.Xml.Serialization;
using System.IO;

namespace AgileShare.Framework.SiteProvisioning.Definitions
{
	// TODO: Need to provide a way to set the Type as text, to be able to create custom field types.
	/// <summary>
	/// The configuration of a field that can be used to add a new field to SharePoint. The DefinitionXml property returns the field in a valid SharePoint XML format.
	/// </summary>
	/// <example>	
	/// FieldDefinition fieldDefinition = new FieldDefinition(new Guid("{F56D2B4F-242A-4169-89F9-F3CA0AB33E8A}"), "MyFieldName", "My Field Display Name", "My Field Description", true, "My Group");
	/// 
	/// SPFieldHelper.AddField(fieldDefinition, list.Fields); // Add to SPList fields
	/// SPFieldHelper.AddField(fieldDefinition, web.Fields); // Add to SPWeb fields
	/// </example>
	public class FieldDefinition
	{
		/// <summary>
		/// Gets or sets the ID.
		/// </summary>
		/// <value>
		/// The ID.
		/// </value>
		public Guid ID { get; set; }

		/// <summary>
		/// Gets or sets the type.
		/// </summary>
		/// <value>
		/// The type.
		/// </value>
		public virtual FieldType Type { get; set; }

		/// <summary>
		/// Gets or sets the type as string.
		/// </summary>
		/// <value>
		/// The type as string.
		/// </value>
		public string TypeAsString { get; set; }

		/// <summary>
		/// Gets or sets the name.
		/// </summary>
		/// <value>
		/// The name.
		/// </value>
		public string Name { get; set; }

		/// <summary>
		/// Gets or sets the display name.
		/// </summary>
		/// <value>
		/// The display name.
		/// </value>
		public string DisplayName { get; set; }

		/// <summary>
		/// Gets or sets the description.
		/// </summary>
		/// <value>
		/// The description.
		/// </value>
		public string Description { get; set; }

		/// <summary>
		/// Gets or sets a value indicating whether this <see cref="FieldDefinition"/> is required.
		/// </summary>
		/// <value>
		///   <c>true</c> if required; otherwise, <c>false</c>.
		/// </value>
		public bool Required { get; set; }

		/// <summary>
		/// Gets or sets the group.
		/// </summary>
		/// <value>
		/// The group.
		/// </value>
		public string Group { get; set; }

		/// <summary>
		/// Additional XElements or XAttributes to append to the Field Definition.
		/// </summary>
		/// <value>
		/// Additional XElements or XAttributes to append to the Field Definition.
		/// </value>
		public List<object> AdditionalData { get; set; }

		/// <summary>
		/// Initializes a new instance of the <see cref="FieldDefinition" /> class.
		/// </summary>
		public FieldDefinition()
		{
			this.AdditionalData = new List<object>();
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="FieldDefinition"/> class.
		/// </summary>
		/// <param name="id">The id.</param>
		/// <param name="type">The type.</param>
		/// <param name="name">The name.</param>
		/// <param name="displayName">The display name.</param>
		/// <param name="description">The description.</param>
		/// <param name="required">if set to <c>true</c> [required].</param>
		/// <param name="group">The group.</param>
		/// <param name="additionalData">The additional data.</param>
		public FieldDefinition(Guid id, FieldType type, string name, string displayName, string description, bool required, string group, params object[] additionalData)
		{
			this.ID = id;
			this.Type = type;
			this.Name = name;
			this.DisplayName = displayName;
			this.Description = description;
			this.Required = required;
			this.Group = group;
			this.AdditionalData = additionalData.ToList();
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="FieldDefinition"/> class.
		/// </summary>
		/// <param name="id">The id.</param>
		/// <param name="type">The type.</param>
		/// <param name="name">The name.</param>
		/// <param name="displayName">The display name.</param>
		/// <param name="description">The description.</param>
		/// <param name="required">if set to <c>true</c> [required].</param>
		/// <param name="group">The group.</param>
		/// <param name="additionalData">The additional data.</param>
		public FieldDefinition(Guid id, string type, string name, string displayName, string description, bool required, string group, params object[] additionalData)
			: this(id, FieldType.None, name, displayName, description, required, group, additionalData)
		{
			this.TypeAsString = type;
		}

		/// <summary>
		/// Gets the definition XML.
		/// </summary>
		public virtual XElement DefinitionXml
		{
			get
			{
				// XElement element = SPFieldHelper.GetFieldXml(this.ID, this.Type, this.Name, this.DisplayName, this.Description, this.Required, this.Group);

				string fieldType = (this.Type != FieldType.None) ? this.Type.ToString() : this.TypeAsString;

				XElement field = new XElement("Field",
					new XAttribute("ID", this.ID),
					new XAttribute("Type", fieldType),
					new XAttribute("Name", this.Name),
					new XAttribute("StaticName", this.Name),
					new XAttribute("DisplayName", this.DisplayName),
					new XAttribute("Description", this.Description),
					new XAttribute("Required", this.Required.ToString().ToUpper()),
					new XAttribute("Group", this.Group));

				foreach (object data in AdditionalData)
				{
					field.Add(data);
				}
				return field;
			}
		}

		/// <summary>
		/// Returns a <see cref="System.String" /> that represents this instance.
		/// </summary>
		/// <returns>
		/// A <see cref="System.String" /> that represents this instance.
		/// </returns>
		public override string ToString()
		{
			return this.DefinitionXml.ToString();
		}

		protected void AddAttributeIfNotNull(XElement field, string attributeName, object value)
		{
			if (value != null)
			{
				field.Add(new XAttribute(attributeName, value));
			}
		}
	}
}
